import { LinkedListNode } from "../common/linked_list";
import { Command } from "../protocol/token.service";
import { InputElement, SchemaElement } from "./schema-element";
import { moveToBeforeElement, stringifyElement } from "../type-server/schema-util";

export interface ElementLog {
    undo: () => void;
    stringify: () => string;
}

export class ElementLogManager implements ElementLog {
    private logs: ElementLog[] = [];

    addLog(log: ElementLog) {
        this.logs.push(log);
    }

    undo() {
        const log = this.logs.pop();
        if (log) {
            log.undo();
        }
    }

    stringify(): string {
        return this.logs.map(log => log.stringify()).join('\n');
    }
}

/**
 * 聚焦元素日志
 */
export class FocusElementLog implements ElementLog {
    element: InputElement;
    constructor(element: InputElement) {
        this.element = element;
    }

    undo() {
      // nop  
    }
    stringify() {
        return `focus element: ${stringifyElement(this.element)}`;
    }
}

/**
 * 新增元素的日志
 */
export class AddElementLog implements ElementLog {
    element: SchemaElement;
    node: LinkedListNode<SchemaElement>;
    constructor(element: SchemaElement, node: LinkedListNode<SchemaElement>) {
        this.element = element;
        this.node = node;
    }

    stringify(): string {
        return `add element: ${stringifyElement(this.element)}`;
    }

    undo() {
        const parentElement = this.element.parentElement!;
        parentElement.removeChildNode(this.node);
    }
}

/**
 * 删除元素的日志
 * element删除后，其index不变化
 * node的prev和next变为null
 * parentElement
 */
export class RemoveElementLog implements ElementLog {
    currentNode: LinkedListNode<SchemaElement>;
    // move指令，需要提前生成
    commands: Command[];

    constructor(currentNode: LinkedListNode<SchemaElement>) {
        this.currentNode = currentNode;
        const commands: Command[] = [];
        moveToBeforeElement(commands, currentNode.value, currentNode);
        this.commands = commands;
    }

    stringify(): string {
        return `remove element: ${stringifyElement(this.currentNode.value)}`;
    }

    undo() {
        const currentNode = this.currentNode;
        const parentElement = currentNode.value.parentElement!;
        parentElement.insertChildNode(currentNode, currentNode.prev ? currentNode.prev : undefined);
    }
}

/**
 * 更新元素value的日志
 */
export class UpdateElementLog implements ElementLog {
    element: InputElement;
    prevValue: string;

    constructor(element: InputElement, prevValue: string) {
        this.element = element;
        this.prevValue = prevValue;
    }

    stringify(): string {
        return `update element: ${stringifyElement(this.element)}, prevValue: ${this.prevValue}`;
    }

    undo() {
        this.element.inputValue = this.prevValue;
    }
}

/**
 * 撤销操作
 * @param logs 日志
 */
export const undo = (logs: ElementLog[]): void => {
    for (let i = logs.length - 1; i >= 0; i--) {
        logs[i].undo();
    }
}
